/*
 * Copyright 2023 BSC*
 * *Barcelona Supercomputing Center (BSC)
 * 
 * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
 * 
 * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you
 * may not use this file except in compliance with the License, or, at your
 * option, the Apache License version 2.0. You may obtain a copy of the
 * License at
 * 
 * https://solderpad.org/licenses/SHL-2.1/
 * 
 * Unless required by applicable law or agreed to in writing, any work
 * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

/* Top view of the branch predictor module
 * 
 * Describes the different components of the branch prediction unit and 
 * how they are connected. It is composed by Bimodal Predictor and Return
 * Addres Stack.
 */
module branch_predictor
    import drac_pkg::*;
    import riscv_pkg::*;
(
    input wire           clk_i,                         // Clock input signal
    input wire           rstn_i,                        // Reset negate input signal
    input addrPC_t       pc_fetch_i,                    // Program counter value at Fetch Stage
    input addrPC_t       pc_execution_i,                // Program counter at Execution Stage
    input addrPC_t       branch_addr_result_exec_i,     // Address generated by branch in Execution Stage
    input wire           branch_taken_result_exec_i,    // Taken or not taken branch result in Execution Stage
    input wire           is_branch_EX_i,                // The instruction in the Execution Stage is a branch

    output logic         branch_predict_is_branch_o,    // Bit that encodes if the instruction being fetched is a branch
    output logic         branch_predict_taken_o,        // Bit that encodes branch taken '1' or not '0'
    output addrPC_t      branch_predict_addr_o          // Address predicted to jump
);

 // Number of entries of the is branch predictor.
localparam NUM_IS_BRANCH_ENTRIES = 128; 

// Tags stored in is_branch_table
typedef logic [39 - MOST_SIGNIFICATIVE_INDEX_BIT_BP - 1  : 0] tag;
typedef reg   [39 - MOST_SIGNIFICATIVE_INDEX_BIT_BP - 1  : 0] tag_reg;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////// Declaration of local signals and modules                                               /////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                
wire            bimodal_predict_taken;
addrPC_t        bimodal_predict_addr;
tag             is_branch_tag;
wire            is_branch_prediction;
logic           is_branch_tag_valid;
                
// Instatiation of bimodal predictor
bimodal_predictor bimodal_predictor_inst(
    .clk_i (clk_i),
    .rstn_i (rstn_i),
    .pc_fetch_i (pc_fetch_i),
    .pc_execution_i (pc_execution_i),
    .branch_addr_result_exec_i (branch_addr_result_exec_i),
    .branch_taken_result_exec_i (branch_taken_result_exec_i),
    .is_branch_EX_i (is_branch_EX_i),
    .bimodal_predict_taken_o (bimodal_predict_taken),
    .bimodal_predict_addr_o (bimodal_predict_addr)
);

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////// Instantiation of is branch predictor                                                   /////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    logic [39 - MOST_SIGNIFICATIVE_INDEX_BIT_BP - 1  : 0] is_branch_table [0 : NUM_IS_BRANCH_ENTRIES-1]; 
    logic                                                 is_branch_table_valid [0 : NUM_IS_BRANCH_ENTRIES-1];


`ifdef QUESTASIM     
	initial begin
        for (int i = 0; i < NUM_IS_BRANCH_ENTRIES; i++) begin
             is_branch_table[i] = $urandom_range(268435455);
        end
	end
`endif


`ifdef VERILATOR     
	initial begin
        for (int i = 0; i < NUM_IS_BRANCH_ENTRIES; i++) begin
             is_branch_table[i] = 32'hFFFFFFFF;
        end
	end
`endif

    always_comb 
    begin
        is_branch_tag = is_branch_table[pc_fetch_i[MOST_SIGNIFICATIVE_INDEX_BIT_BP:LEAST_SIGNIFICATIVE_INDEX_BIT_BP]];
        is_branch_tag_valid = is_branch_table_valid[pc_fetch_i[MOST_SIGNIFICATIVE_INDEX_BIT_BP:LEAST_SIGNIFICATIVE_INDEX_BIT_BP]];
    end
    
    always @(posedge clk_i, negedge rstn_i) 
    begin
        if(~rstn_i)begin
            for (int i = 0; i < NUM_IS_BRANCH_ENTRIES; i++) begin
                is_branch_table_valid[i] <= 1'b0;
            end
        end else if(is_branch_EX_i) begin
            is_branch_table[pc_execution_i[MOST_SIGNIFICATIVE_INDEX_BIT_BP:LEAST_SIGNIFICATIVE_INDEX_BIT_BP]] <= pc_execution_i[39:MOST_SIGNIFICATIVE_INDEX_BIT_BP+1];
            is_branch_table_valid[pc_execution_i[MOST_SIGNIFICATIVE_INDEX_BIT_BP:LEAST_SIGNIFICATIVE_INDEX_BIT_BP]] <= 1'b1;
        end
    end



assign is_branch_prediction = (is_branch_tag == pc_fetch_i[39:MOST_SIGNIFICATIVE_INDEX_BIT_BP+1] && is_branch_tag_valid);


// MUX that decides wheter the predicted address comes from Bimodal
assign branch_predict_addr_o = bimodal_predict_addr;

// MUX that decides wheter the branch prediction is comes from Bimodal 
assign branch_predict_taken_o = bimodal_predict_taken;

// MUX that decides whteter the fetched instruction is a branch or not
assign branch_predict_is_branch_o = is_branch_prediction;

endmodule
